package com.jse.lang.object;

import org.apache.commons.lang3.builder.ReflectionToStringBuilder;
import org.junit.Assert;
import org.junit.Test;

/**
 * 原型模式：就是针对复制对象的重复构建，实现clone复制粘贴过来
 * 
 * 参考：http://www.iteye.com/topic/483469
 * 浅拷贝：1级拷贝（引用指向同一个对象）
 * 深拷贝：多级拷贝，对象里面有引用类型的还要层级拷贝，指向不同的对象
 * 
 * @author zenghua233
 *
 */
public class ObjectCloneTest {

	/**
	 * 浅拷贝测试，只复制引用
	 * @throws Exception
	 */
	@Test
	public void testShallowClone() throws Exception {
		// user.  
		User user = new User();  
		user.name = "user";  
		user.age = 20;  
		// account.  
		Account account = new Account();  
		account.user = user;  
		account.balance = 10000;  
		// copy.  
		Account copy = (Account) account.clone();  
		  
		// balance因为是primitive，所以copy和原型是相等且独立的。  
		Assert.assertEquals(copy.balance, account.balance);  
		copy.balance = 20000;  
		// 改变copy不影响原型。  
		Assert.assertTrue(copy.balance != account.balance);  
		  
		// user因为是引用类型，所以copy和原型的引用是同一的。  
		Assert.assertTrue(copy.user == account.user);  
		copy.user.name = "newName";  
		// 改变的是同一个东西。  
		Assert.assertEquals("newName", account.user.name);  
	}
	
	@Test
	public void testShallowClone1() throws Exception {
		// user.  
		User user = new User();  
		user.name = "user";  
		user.age = 20;  
		  
		// copy  
		User copy = (User) user.clone();  
		  
		// age因为是primitive，所以copy和原型是相等且独立的。  
		Assert.assertEquals(copy.age, user.age);  
		copy.age = 30;  
		// 改变copy不影响原型。  
		Assert.assertTrue(copy.age != user.age);  
		  
		// name因为是引用类型，所以copy和原型的引用是同一的。  
		Assert.assertTrue(copy.name == user.name);
		// String为不可变类。没有办法可以通过对copy.name的字符串的操作改变这个字符串。  
		// 改变引用新的对象不会影响原型。  
		copy.name = "newname";  
		Assert.assertEquals("newname", copy.name);
		Assert.assertEquals("user", user.name);
	}
	
	/**
	 * 如果拷贝的对象中还有引用类型，这个引用类型也要实现Cloneable，执行clone方法，再把克隆的值赋值到对象，参考Account1
	 * @throws Exception
	 */
	@Test
	public void testDeepCopy() throws Exception {
		User user = new User();
		user.name = "a";
		user.age = 1;
		Account1 account = new Account1();
		account.balance = 1;
		account.user = user;
		
		Account1 copyAccount = (Account1) account.clone();
		copyAccount.user.name = "b";
		copyAccount.user.age = 2;
		System.out.println("original: " + account.user.name + "-" + account.user.age);
		System.out.println("copy : " + copyAccount.user.name + "-" + copyAccount.user.age);
	}
}

class User implements Cloneable {
	String name;
	int age;
	
	@Override  
    public Object clone() throws CloneNotSupportedException {  
        return super.clone();  
    }  
}

class Account implements Cloneable {
	User user;
	long balance;

	@Override
	public Object clone() throws CloneNotSupportedException {
		return super.clone();
	}
}

class Account1 implements Cloneable {
	User user;
	long balance;

	@Override
	public Object clone() throws CloneNotSupportedException {
		Account1 account = (Account1) super.clone();
		account.user = (User) user.clone();
		return account;
	}
}
